package com.shockweb.common.serializable.xml;


import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.shockweb.common.International;
import com.shockweb.common.serializable.SerializableObject;
import com.shockweb.common.utils.StringUtil;
import com.shockweb.common.utils.classmethod.ClassMethod;
import com.shockweb.common.utils.classmethod.ClassMethodDefine;

/**
 * 通用xml写入器，将对象实例按一定格式写成xml
 * @author: 彭明华
 * 2017年5月18日  彭明华创建
 *
 */
public class XmlWriter{
	
	/**
	 * 使用unicode转码
	 */
	private boolean unicode = false;
	/**
	 * null
	 */
	public static final String NULL="|null|";
	/**
	 * 空字符串
	 */
	public static final String EMPTY ="|empty|";
	   
	/**
	 * 设置是否使用unicode转码
	 * @param unicode
	 */
	public void setUnicode(boolean unicode){
		this.unicode = unicode;
	}
	
	/**
	 * xml的版本
	 */
	private String version = "1.0";
	/**
	 * 设置Xml的version
	 * @param version
	 */
	public void setVersion(String version){
		this.version = version;
	}
	
	/**
	 * 读取xml第一个标签的名称
	 */
	private String rootName = null;
	/**
	 * 设置读取xml第一个标签的名称
	 * @param rootName
	 */
	public void setRootName(String rootName){
		this.rootName = rootName;
	}
	
	/**
	 * 不写入xml的元素
	 */
	private List<String> notWrites = null;
	/**
	 * 构造方法
	 */
	public XmlWriter(){
	}	
	/**
	 * 构造方法
	 * @param notWrites 不写入xml的元素
	 */
	public XmlWriter(List<String> notWrites){
		this.notWrites = notWrites;
	}

	/**
	 * 构造方法
	 * @param unicode 是否转化成unicode
	 */
	public XmlWriter(boolean unicode){
		this(unicode,null,null);
	}

	/**
	 * 构造方法
	 * @param notWrites 不写入xml的元素
	 * @param unicode 是否转化成unicode
	 */
	public XmlWriter(List<String> notWrites,boolean unicode){
		this(notWrites,unicode,null,null);
	}
	/**
	 * 构造方法
	 * @param version xml版本
	 */
	public XmlWriter(String version){
		this(version,null);
	}
	
	/**
	 * 构造方法
	 * @param unicode 是否转化成unicode
	 * @param version xml版本
	 */
	public XmlWriter(boolean unicode,String version){
		this(unicode,version,null);
	}
	

	/**
	 * 构造方法
	 * @param notWrites 不写入xml的元素
	 * @param unicode 是否转化成unicode
	 * @param version xml版本
	 */
	public XmlWriter(List<String> notWrites,boolean unicode,String version){
		this(notWrites,unicode,version,null);
	}
	
	/**
	 * 构造方法
	 * @param version xml版本
	 * @param rootName xml根元素
	 */
	public XmlWriter(String version,String rootName){
		this(null,version,rootName);
	}
	/**
	 * 构造方法
	 * @param unicode 是否转化成unicode
	 * @param version xml版本
	 * @param rootName xml根元素
	 */
	public XmlWriter(boolean unicode,String version,String rootName){
		this(null,unicode,version,rootName);
	}
	/**
	 * 构造方法
	 * @param notWrites 不写入xml的元素
	 * @param version xml版本
	 * @param rootName
	 */
	public XmlWriter(List<String> notWrites,String version,String rootName){
		this(notWrites,false,version,rootName);
	}
	/**
	 * 构造方法
	 * @param notWrites 不写入xml的元素
	 * @param unicode 是否转化成unicode
	 * @param version xml版本
	 * @param rootName xml根元素
	 */
	public XmlWriter(List<String> notWrites,boolean unicode,String version,String rootName){
		this.unicode = unicode;
		this.notWrites = notWrites;
		if(version!=null)
			this.version = version;
		if(rootName!=null)
			this.rootName = rootName;
	}
    
	/**
	 * 将对象instance解析成xml写到xmlFile路径的文件中
	 * @param instance 对象实例
	 * @param xmlFile 文件
	 * @throws XmlException
	 */
    public void writeFile(Object instance,String xmlFile)throws XmlException
    {
        FileOutputStream f = null; 
        try
        {
        	f = new FileOutputStream(xmlFile);
        	writeStream(instance,f);
        }
        catch(Exception e)
        {
            throw new XmlException(e);
        }finally{
        	if(f!=null){
        		try {
					f.close();
					f = null;
				} catch (IOException e) {
				}
        	}
        }    	
    }
	
    /**
     * 将对象instance解析成xml到InputStream返回
     * @param instance 对象实例
     * @return InputStream 数据流
     * @throws XmlException
     */
    public InputStream writeStream(Object instance)throws XmlException
    {
    	try {
			return new ByteArrayInputStream(write(instance).toString().getBytes(International.CHARSET));
		} catch (UnsupportedEncodingException e) {
			throw new XmlException(e);
		}
    }
	
    /**
     * 将对象instance解析成xml写到stream中
     * @param instance 对象实例
     * @param stream 输出的数据流
     * @throws XmlException
     */
    public void writeStream(Object instance,OutputStream stream)throws XmlException
    {
		try {
			stream.write(write(instance).toString().getBytes(International.CHARSET));
		} catch (IOException e) {
			throw new XmlException(e);
		}
    }
	
    /**
     * 将对象instance解析成xml写到String返回
     * @param instance 对象实例
     * @return String xml字符串
     * @throws XmlException
     */
    public String writeString(Object instance)throws XmlException
    {
    	return write(instance).toString();
    }
    
    /**
     * 将对象instance解析成xml写到String中，不带xml头
     * @param instance 对象实例
     * @return String xml字符串
     * @throws XmlException
     */
    public String writeStringNoTitle(Object instance)throws XmlException
    {
    	StringBuffer sb = writeNoTitle(instance);
    	if(sb!=null){
    		return sb.toString();
    	}else{
    		return null;
    	}
    }
    
	/**
	 * 将value转成xml
	 * @param value 数值
	 * @return String 转义成xml字符串
	 */
    public static final String convertToXml(String value){
    	if(value == null){
    		return null;
    	}
		int len = value.length();
		char content[] = new char[len];
		value.getChars(0, len, content, 0);		
		StringBuffer result = new StringBuffer(len + 50);
		for (int i = 0; i < len; i++)
			switch (content[i]) {
				case 60 : // '<'
					result.append("&lt;");
					break;

				case 62 : // '>'
					result.append("&gt;");
					break;

				case 38 : // '&'
					result.append("&amp;");
					break;

				case 34 : // '"'
					result.append("&quot;");
					break;

//				case 39 : // '\''
//					result.append("&#39;");
//					break;

				default :
					result.append(content[i]);
					break;
			}
		return result.toString();
    }

	/**
	 * 将value转成xml
	 * @param value 数值
	 * @param unicode 是否转化成unicode
	 * @return String 转义成xml字符串
	 */
	public static final String convertToXml(String value,boolean unicode){
		if(unicode){
			return StringUtil.toUnicode(value);
		}else{
			return convertToXml(value);
		}
	}
	
	/**
	 * 格式化成xml合法的字符串
	 * @param v 数值
	 * @param unicode 是否转化成unicode
	 * @return String 转义成xml字符串
	 */
	public static final String format(Object v,boolean unicode) {
		String value = null;
		if(v == null){
			return NULL;
		}else if(v instanceof String){
		    if("".equals(v)){
		        value = convertToXml(EMPTY,unicode);
		    }else{
		        value = ((String) v).replaceAll("\\|", "\\|\\|");
		        value = convertToXml(value,unicode);
		    }
		}else if(v instanceof StringBuilder){
		    String temp = ((StringBuilder)v).toString();
		    if("".equals(temp)){
                value = convertToXml(EMPTY,unicode);
            }else{
                value = temp.replaceAll("\\|", "\\|\\|");
                value = convertToXml(value,unicode);
            }
		}else if(v instanceof StringBuffer){
		    String temp = ((StringBuffer)v).toString();
            if("".equals(temp)){
                value = convertToXml(EMPTY,unicode);
            }else{
                value = temp.replaceAll("\\|", "\\|\\|");
                value = convertToXml(value,unicode);
            }
		}else if(v instanceof Date){
			SimpleDateFormat sdf = new SimpleDateFormat(International.OUT_DATETIME);
			value = sdf.format(v);
		}else if(v instanceof Time){
			SimpleDateFormat sdf = new SimpleDateFormat(International.OUT_TIME);
			value = sdf.format(v);
		}else if(v instanceof Timestamp){
			SimpleDateFormat sdf = new SimpleDateFormat(International.OUT_DATETIMEMSEL);
			value = sdf.format(v);
		}else if(v instanceof java.util.Date){
			SimpleDateFormat sdf = new SimpleDateFormat(International.OUT_DATETIME);
			value = sdf.format(v);
		}else if(v instanceof byte[]){
			value = SerializableObject.base64EncodeString((byte[])v);
		}else{
			if(isCustomClass(v.getClass())){
				value = convertToXml(v.toString(),unicode);
			}else{
				value = v.toString();
			}	
		}
		return value;
	}

	
    /**
     * 判断是否用户自定义类
     * @param cls class定义
     * @return boolean 如果是用户自定义类返回true，否则为false
     */
    private static boolean isCustomClass(Class<?> cls){
        if(String.class.equals(cls))
            return false;
        if(StringBuffer.class.equals(cls))
            return false;
        if(BigDecimal.class.equals(cls))
            return false;
        if(double.class.equals(cls) || Double.class.equals(cls))
            return false;
        if(float.class.equals(cls) || Float.class.equals(cls))
            return false;
        if(int.class.equals(cls) || Integer.class.equals(cls))
            return false;
        if(long.class.equals(cls) || Long.class.equals(cls))
            return false;
        if(short.class.equals(cls) || Short.class.equals(cls))
            return false;
        if(boolean.class.equals(cls) || Boolean.class.equals(cls))
            return false;
        if(byte.class.equals(cls) || Byte.class.equals(cls))
            return false;
        if(char.class.equals(cls) || Character.class.equals(cls))
            return false;
        if(Date.class.equals(cls))
            return false;
        if(Time.class.equals(cls))
            return false;
        if(Timestamp.class.equals(cls))
            return false;
        if(java.util.Date.class.equals(cls))
            return false;
        if(boolean.class.equals(cls) || Boolean.class.equals(cls))
            return false;
        if(BigInteger.class.equals(cls))
            return false;
        if(StringBuilder.class.equals(cls))
            return false;
        if(byte[].class.equals(cls))
            return false;
        return true;
    }
    
    
    /**
     * 判断instance的name是否可写入
     * @param instance 对象实例
     * @param name 名称
     * @return boolean 能写入放回true，否则为false
     */
    private boolean isNoWrite(Object instance,String name){
    	String inName = instance.getClass().getName().toLowerCase() + "." + name;
    	if(notWrites!=null){
    		if(notWrites.contains(inName)){
    			return true;
    		}
    		if(notWrites.contains(name)){
    			return true;
    		}
    	}else{
    		return false;
    	}
    	return false;
    }
    
    
    /**
     * 将对象解析成xml写到StringBuffer中
     * @param instance 对象实例
     * @return StringBuffer 放回字符
     * @throws XmlException
     */
    public StringBuffer write(Object instance) throws XmlException
    {
    	StringBuffer rtn = new StringBuffer("<?xml version=\"").append(version).
			append("\" encoding=\"").append(International.CHARSET).append("\"?>");
    	StringBuffer sb = writeNoTitle(instance);
    	if(sb!=null){
    		rtn.append(sb);
    	}else{
    		rtn = null;
    	}
    	return rtn;
    }
    
    /**
     * 将对象解析成xml写到StringBuffer中，不包括xml头
     * @param instance 对象实例
     * @return StringBuffer 放回字符
     * @throws XmlException
     */
    public StringBuffer writeNoTitle(Object instance)throws XmlException{
    	StringBuffer rtn = new StringBuffer();
		int startLen = rtn.length();
		try {
			if(instance==null){
				return null;
			}
			if(rootName==null){
				if(instance  instanceof List<?>){
					rtn.append("<" + instance.getClass().getSimpleName() + ">");
					writeList((List<?>)instance,rtn,"list");
					rtn.append("\n</" + instance.getClass().getSimpleName() + ">");
	    		}else if(instance instanceof Map<?,?>){
	    			rtn.append("<" + instance.getClass().getSimpleName() + ">");
	    			writeMap((Map<?,?>)instance,rtn,"map");
	    			rtn.append("\n</" + instance.getClass().getSimpleName() + ">");
	    		}else if(isCustomClass(instance.getClass())){
					writeValue(instance,rtn,instance.getClass().getSimpleName());
				}else{
	    			rtn.append("<" + instance.getClass().getSimpleName() + "><value>").append(format(instance,unicode)).append("</value></" + instance.getClass().getSimpleName() + ">");
				}
			}else{
				if(instance instanceof List<?>){
					writeList((List<?>)instance,rtn,rootName);
	    		}else if(instance instanceof Map<?,?>){
	    			writeMap((Map<?,?>)instance,rtn,rootName);
	    		}else if(isCustomClass(instance.getClass())){
	    			writeValue(instance,rtn,rootName);
				}else{
	    			rtn.append("\n<" + rootName + "><value>").append(format(instance,unicode)).append("</value></" + rootName + ">");
				}
			}
		} catch (Exception e) {
			throw new XmlException(e);
		}
		if(rtn.length()==startLen){
			rtn = null;
		}
		return rtn;
    }
    
    /**
     * 默认的xml头信息
     */
    public static String TITLE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
    

    /**
     * 类的方法定义
     */
    private Map<String,List<ClassMethodDefine>> cmdses = new HashMap<String,List<ClassMethodDefine>>();
    
    /**
     * 将对象instance解析成xml写到StringBuffer中
     * @param instance 普通对象实例
     * @param sb 待写入的StringBuffer
     * @param rootName 根元素名称
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    private void writeValue(Object instance,StringBuffer sb,String rootName) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{
    	if(instance!=null && !isNoWrite(instance,rootName)){
        	if(isCustomClass(instance.getClass())){
            	List<ClassMethodDefine> cmds = cmdses.get(instance.getClass().getName());
            	if(cmds==null){
            		cmds = ClassMethod.getGetMethods(instance.getClass());
            		for(int i=cmds.size()-1;i>=0;i--){
            			ClassMethodDefine cmd = cmds.get(i);
            			if(isNoWrite(instance,cmd.getName())){
            				cmds.remove(cmd);
            			}
            		}
            		cmdses.put(instance.getClass().getName(), cmds);
            	}
        		int startLen1 = sb.length();
        		sb.append("\n<").append(rootName).append('>');
        		int startLen2 = sb.length();
        		boolean flag = false;
        		for(ClassMethodDefine cmd:cmds){
        			Object value = cmd.getMethod().invoke(instance);
        			if(value!=null){
        				if(!isCustomClass(value.getClass())){
    						sb.append("\n<").append(cmd.getName()).append('>').
    						append(format(value,unicode)).
    						append("</").append(cmd.getName()).append('>');						
        				}else{
        					flag = true;
        				}
        			}else{
						sb.append("\n<").append(cmd.getName()).append('>').
						append(format(value,unicode)).
						append("</").append(cmd.getName()).append('>');
        			}
        		}
        		if(flag){		
        			for(ClassMethodDefine cmd:cmds){
        				Object value = cmd.getMethod().invoke(instance);
        				if(value!=null){
        					if(isCustomClass(value.getClass())){
        						if(value instanceof List<?>){
        							writeList((List<?>)value,sb,cmd.getName());
        			    		}else if(value instanceof Map<?,?>){
        			    			writeMap((Map<?,?>)value,sb,cmd.getName());
        						}else{
        							writeValue(value,sb,cmd.getName());
        						}
        					}
        				}
        			}
        		}
        		if(sb.length()==startLen2){
        			sb.delete(startLen1, sb.length());
        		}else{
    				sb.append("\n</").append(rootName).append('>');
        		}
        	}else{
        		sb.append("\n<" + rootName + ">").append(format(instance,unicode)).append("</" + rootName + ">");
        	}
    	}
	}
    
    /**
     * 将List对象lists解析成xml写到StringBuffer中
     * @param lists List对象实例
     * @param sb 待写入的StringBuffer
     * @param rootName 根元素名称
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    private void writeList(List<?> lists,StringBuffer sb,String rootName) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{	
    	sb.append("\n<").append(rootName).append(">");
    	for(Object value:lists){
    		if(value instanceof List<?>){
    			writeList((List<?>)value,sb,"list");
    		}else if(value instanceof Map<?,?>){
    			writeMap((Map<?,?>)value,sb,"map");
    		}else{
    			writeValue(value,sb,"value");
    		}
    	}
    	sb.append("\n</").append(rootName).append(">");
    }
	
    /**
     * 将map对象maps解析成xml写到StringBuffer中
     * @param maps Map对象实例
     * @param sb 待写入的StringBuffer
     * @param rootName 根元素名称
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    private void writeMap(Map<?,?> maps,StringBuffer sb,String rootName) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{	
    	Iterator<?> it = maps.keySet().iterator();
    	sb.append("\n<").append(rootName).append(">");
    	while(it.hasNext()){
    		sb.append("\n<entry>");
    		Object key = it.next();
			writeValue(key,sb,"key");
    		Object value = maps.get(key);
    		if(value instanceof List<?>){
    			writeList((List<?>)value,sb,"value");
    		}else if(value instanceof Map<?,?>){
    			writeMap((Map<?,?>)value,sb,"value");
    		}else{
    			writeValue(value,sb,"value");
    		}
    		sb.append("\n</entry>");
    	}
    	sb.append("\n</").append(rootName).append(">");
    }

}